package org.syncsoft.overalt.gui.api.window;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JSeparator;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.text.JTextComponent;

import org.syncsoft.overalt.common.api.Resource;
import org.syncsoft.overalt.gui.api.WindowContextValue;
import org.syncsoft.overalt.gui.api.componentPlugin.IComponentPlugin;
import org.syncsoft.overalt.gui.impl.componentPlugin.ComboBoxComponentPlugin;
import org.syncsoft.overalt.gui.impl.componentPlugin.TextComponentPlugin;
import org.syncsoft.overalt.gui.util.BuildFormWindow;

/**
 * swing abstract window
 * @author tptfc
 *
 */
public abstract class AbstractWindow {
	private JFrame frame;
	private JPanel panel;
	private boolean created;
	private boolean createMenu;
	
	private AbstractWindowController<?> controller;
	
	private Map<Class<? extends IComponentPlugin>, IComponentPlugin> componentPluginsIndex;
	
	static {
		JFrame.setDefaultLookAndFeelDecorated(false);
		setLookAndFeel();
	}
	
	{
		setComponentPluginsIndex(new HashMap<Class<? extends IComponentPlugin>, IComponentPlugin>());
		setFrame(new JFrame());
		getFrame().setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setCreateMenu(true);
	}

	public AbstractWindow(AbstractWindowController<?> controller) {
		this(500, 550, controller);
	}
	
	public AbstractWindow(int width, int height, AbstractWindowController<?> controller) {
		setSize(width, height);
		setController(controller);
	}
	
	/**
	 * build the window
	 */
	protected abstract void buildWindow();
	
	/**
	 * update window state
	 * @param context
	 */
	public void update(WindowContextValue context) {
	}
	
	/**
	 * set window title
	 * @param title
	 */
	public void setTitle(String title) {
		getFrame().setTitle(title);
	}
	
	/**
	 * show the window
	 */
	public void show() {
		if (!isCreated()) {
			create();
		}
		
		// FIX TO LINUX
		timeWait(350);
		
		getFrame().setVisible(true);
	}
	
	/**
	 * 
	 * @return
	 */
	public boolean isOpened() {
		return getFrame().isEnabled() && getFrame().isVisible();
	}
	
	/**
	 * close the window
	 */
	public void close() {
		getFrame().dispose();
	}
	
	/**
	 * 
	 * @param width
	 * @param height
	 */
	public void setSize(int width, int height) {
		getFrame().setSize(width, height);
	}
	
	/**
	 * 
	 * @param enabled
	 */
	public void setEnable(boolean enabled) {
		getFrame().setEnabled(enabled);
	}
	
	/**
	 * @postcondition JFrame created with all components to show
	 */
	protected void create() {
		getFrame().setLayout(new BorderLayout());
		
		setPanel(new JPanel());
		getFrame().add(getPanel(), BorderLayout.CENTER);
		buildWindow();
		
		if (isCreateMenu()) {
			getFrame().setJMenuBar(createMenuBar());
		}
		
		setCreated(true);
	}
	
	/**
	 * @postcondition create a menu bar
	 * @return
	 */
	protected JMenuBar createMenuBar() {
		JMenuBar menuBar = new JMenuBar();
		
		JMenu fileMenu = new JMenu();
		fileMenu.setText(Resource.getProperty("overalt.label.menu.file"));
		fileMenu.setMnemonic(KeyEvent.VK_F);
		
		menuBar.add(fileMenu);
		
		
		JMenuItem quit = new JMenuItem();
		quit.setText(Resource.getProperty("overalt.label.menu.quit"));
		addAcceleratorAction(quit, Resource.getProperty("overalt.label.menu.quit.accelerator.letter"));
		
		quit.addActionListener(new QuitListener());
		
		fileMenu.add(quit);
		
		return menuBar;
	}

	/**
	 * initialize the componentPlugins
	 */
	protected void initComponentPlugins() {
		addComponentPlugin(new TextComponentPlugin());
		addComponentPlugin(new ComboBoxComponentPlugin());
	}
	
	/**
	 * @precondition componentPlugin != null
	 * @param componentPlugin
	 */
	protected void addComponentPlugin(IComponentPlugin componentPlugin) {
		if (componentPlugin != null) {
			getComponentPluginsIndex().put(componentPlugin.getClass(), componentPlugin);
		}
	}
	
	/**
	 * 
	 * @param <T>
	 * @param clazz
	 * @return
	 */
	@SuppressWarnings("unchecked")
	protected <T extends IComponentPlugin> T getComponentPlugin(Class<T> clazz) {
		T component = (T) getComponentPluginsIndex().get(clazz);
		
		if (component == null) {
			try {
				component = clazz.newInstance();
				addComponentPlugin(component);
			} catch (Exception e) {
				component = null;
			} 
		}
		
		return component;
	}
	
	/**
	 * 
	 * @param key
	 * @return
	 */
	protected JTextField newTextField(String key) {
		return getComponentPlugin(TextComponentPlugin.class).newTextField(key);
	}
	
	/**
	 * 
	 * @param key
	 * @return
	 */
	protected JTextArea newTextArea(String key) {
		return getComponentPlugin(TextComponentPlugin.class).newTextArea(key);
	}
	
	/**
	 * 
	 * @param key
	 * @return
	 */
	protected JPasswordField newPasswordField(String key) {
		return getComponentPlugin(TextComponentPlugin.class).newPasswordField(key);
	}
	
	/**
	 * 
	 * @param key
	 * @return
	 */
	protected JComboBox newComboBox(String key, Collection<?> items) {
		return getComponentPlugin(ComboBoxComponentPlugin.class).newComboBox(key, items);
	}
	
	/**
	 * 
	 * @param key
	 * @return
	 */
	protected JTextComponent getTextComponent(String key) {
		return getComponentPlugin(TextComponentPlugin.class).getTextComponent(key);
	}
	
	/**
	 * @postcondition submit the values data to controller
	 */
	protected void sentoValuesToController() {
		getController().submit(buildContext());
	}
	
	/**
	 * 
	 * @return
	 */
	protected WindowContextValue buildContext() {
		WindowContextValue context = new WindowContextValue();
		
		for (IComponentPlugin component : getComponentPluginsIndex().values()) {
			component.buildContext(context);
		}
		
		return context;
	}

	/**
	 * add default save and cancel buttons
	 * @param build
	 */
	protected void addSaveAndCancelButtons(BuildFormWindow build) {
		build.add(new JSeparator());
		
		JButton commitButton = new JButton(Resource.getProperty("overalt.label.ok"));
		commitButton.addActionListener(new CommitListener());
		JButton cancelButton = new JButton(Resource.getProperty("overalt.label.cancel"));
		cancelButton.addActionListener(new QuitListener());
		
		build.add(cancelButton, commitButton);
	}

	/**
	 * quit action listener
	 * @author tptfc
	 *
	 */
	protected class QuitListener implements ActionListener {
		public QuitListener() {
		}
		
		@Override
		public void actionPerformed(ActionEvent e) {
			getFrame().setVisible(false);
			getFrame().dispose();
		}
	}
	
	/**
	 * commit action listener
	 * @author tptfc
	 *
	 */
	protected class CommitListener implements ActionListener {
		public CommitListener() {
		}
		
		@Override
		public void actionPerformed(ActionEvent e) {
			sentoValuesToController();
		}
	}
	
	/**
	 * @precondition item != null && acceleratorLetter != null && acceleratorLetter.length == 1
	 * @param fileMenu
	 */
	private void addAcceleratorAction(JMenuItem item, String acceleratorLetter) {
		if (item != null && acceleratorLetter != null && acceleratorLetter.length() == 1) {
			int keyValue = (int)acceleratorLetter.charAt(0);
			
			item.setText(item.getText());
			item.setMnemonic(keyValue);
			item.setAccelerator(KeyStroke.getKeyStroke(keyValue, ActionEvent.CTRL_MASK));
		}
	}

	/**
	 * @precondition milliseconds > 100
	 * @postcondition thread will sleep to milliseconds time 
	 */
	private void timeWait(long milliseconds) {
		try {
			Thread.sleep(milliseconds);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * set the loock and feel
	 */
	private static void setLookAndFeel() {
		try {
			UIManager.setLookAndFeel("com.sun.java.swing.plaf.gtk.GTKLookAndFeel");
		} catch (Exception e) {
			try {
				UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
			} catch (Exception e1) {
				try {
					UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
				} catch (Exception e2) {
					// do nothing
				} 
			}
		}
	}
	
	//	GETs AND SETs
	protected JFrame getFrame() {
		return frame;
	}
	protected void setFrame(JFrame frame) {
		this.frame = frame;
	}
	protected JPanel getPanel() {
		return panel;
	}
	protected void setPanel(JPanel panel) {
		this.panel = panel;
	}
	protected AbstractWindowController<?> getController() {
		return controller;
	}
	protected void setController(AbstractWindowController<?> controller) {
		this.controller = controller;
	}
	protected boolean isCreateMenu() {
		return createMenu;
	}
	protected void setCreateMenu(boolean createMenu) {
		this.createMenu = createMenu;
	}
	boolean isCreated() {
		return created;
	}
	void setCreated(boolean created) {
		this.created = created;
	}
	private Map<Class<? extends IComponentPlugin>, IComponentPlugin> getComponentPluginsIndex() {
		return componentPluginsIndex;
	}

	private void setComponentPluginsIndex(Map<Class<? extends IComponentPlugin>, IComponentPlugin> componentPluginsIndex) {
		this.componentPluginsIndex = componentPluginsIndex;
	}
}
